home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Utilities Experience
/
The Utilities Experience - Volume 1.iso
/
software
/
emulation
/
frodo
/
src
/
iec.asm
< prev
next >
Wrap
Assembly Source File
|
1996-01-29
|
29KB
|
1,463 lines
*
* IEC.asm - IEC-Bus-Routinen, 1541-Emulation
*
* Copyright (C) 1994-1996 by Christian Bauer
*
*
* Anmerkungen:
* ------------
*
* Aufrufkonventionen:
* - Die Routinen IECOut#? und IECIn geben in d0 den Status zurück.
* Dieser wird von den aufrufenden Routinen mit dem C64-Statusbyte
* ($90) verodert.
* - Die Routinen dürfen nur d0-d1/a0-a1 verändern
*
* Funktionsweise:
* - Am IEC-Bus gibt es drei Sorten von Geräten: Controller, Listener
* und Talker. Der Controller sind immer wir, zusätzlich können wir
* wahlweise Talker und Listener sein. Es kann immer nur einen Talker
* und einen Listener geben (am echten IEC-Bus sind mehrere Listener
* erlaubt, aber hier nicht).
* - Die Geräte, die angesprochen werden, sind entweder real am IEC-Kabel
* oder werden hier emuliert. Für jedes emulierte Laufwerk existiert
* eine eigene Datenstruktur (Drv?Data). Ein Zeiger darauf wird bei
* Talk/Listen in TalkerData/ListenerData gespeichert und steht den
* nachfolgend aufgerufenen Routinen zur Verfügung, damit sich diese
* um das jeweils angesprochene Laufwerk kümmern.
* - Bei Geräten am Kabel wird #?Data nicht benutzt, da die Routinen
* dann die Daten im wesentlichen einfach an den IEC-Bus durchreichen
*
* EOI/EOF:
* - Im Gegensatz zum Amiga, wo der EOF-Zustand erst nach dem Lesen
* des letzten Bytes erkannt wird, wird beim IEC-Bus das EOF
* gleichzeitig mit dem letzten Byte gesendet. Daher verwenden die
* Leseroutinen einen Puffer, in dem immer ein Byte bereitsteht.
*
* DRVDATA/drv_Type/drv_#?Proc:
* - Jedem Laufwerk (8-11) ist eine Datenstruktur DRVDATA zugeordnet, die
* u.a. den Typ des Laufwerks enthält (Amiga-Dateisystem, .d64-Datei,
* IEC-Kabel).
* - Je nach Typ werden die sechs Prozedurzeiger drv_InitProc, drv_ExitProc,
* drv_OpenProc, drv_CloseProc, drv_ReadProc und drv_WriteProc gesetzt, um
* entweder die Emulation im Amiga-Dateisystem oder in einer .d64-Datei
* auszuwählen. Nur für Geräte am IEC-Kabel erfolgt die Ein-/Ausgabe
* direkt hier, für die anderen Typen ist sie in den Include-Files
* "1541fs.i" und "1541d64.i" gekapselt.
* - Die drv_#?Proc-Zeiger bilden eine transparente Schnittstelle zur
* Implementation eines Gerätes (im Prinzip ließe sich so auch eine
* Druckeremulation einbauen):
* drv_InitProc bereitet die Emulation vor
* drv_ExitProc beendet die Emulation
* drv_OpenProc öffnet einen Kanal
* drv_CloseProc schließt einen Kanal
* drv_ReadProc liest aus einem Kanal
* drv_WriteProc schreibt in einen Kanal
*
* Inkompatibilitäten/Verbesserungen:
* - Es kann nur ein Listener gleichzeitig aktiv sein
*
MACHINE 68020
INCLUDE "exec/types.i"
INCLUDE "exec/macros.i"
INCLUDE "exec/memory.i"
INCLUDE "dos/dos.i"
INCLUDE "dos/dosasl.i"
INCLUDE "resources/misc.i"
INCLUDE "hardware/cia.i"
XREF _SysBase
XREF _DOSBase
XREF _CiaBase
XREF _MiscBase
XREF _UtilityBase
XREF _ciaaprb
XREF _ciaaddrb
XREF _FS_Init ;1541fs.o
XREF _FS_Exit
XREF _FS_Open
XREF _FS_Close
XREF _FS_Read
XREF _FS_Write
XREF _D64_Init ;1541d64.o
XREF _D64_Exit
XREF _D64_Open
XREF _D64_Close
XREF _D64_Read
XREF _D64_Write
XDEF InitIEC
XDEF ExitIEC
XDEF ChangedIECPrefs
XDEF IECOut
XDEF IECOutATN
XDEF IECOutSec
XDEF IECIn
XDEF IECSetATN
XDEF IECRelATN
XDEF IECTurnaround
XDEF IECRelease
XDEF IECIsOpen
XDEF Dir8 ;Prefs
XDEF Dir9
XDEF Dir10
XDEF Dir11
XDEF Drv8Type
XDEF Drv9Type
XDEF Drv10Type
XDEF Drv11Type
XDEF OtherIEC
XDEF MapSlash
XREF PDir8 ;Für ChangedIECPrefs
XREF PDir9
XREF PDir10
XREF PDir11
XREF PDrv8Type
XREF PDrv9Type
XREF PDrv10Type
XREF PDrv11Type
SECTION "text",CODE
**
** Definitionen
**
; 1541-Fehlercodes (genauer: Die Nummer des zugeh. Fehlertextes)
ERR_OK = 0 ;00: OK
ERR_WRITEERROR = 1 ;25: WRITE ERROR
ERR_WRITEPROTECT = 2 ;26: WRITE PROTECT ON
ERR_SYNTAX30 = 3 ;30: SYNTAX ERROR (Unbekannter Befehl)
ERR_SYNTAX33 = 4 ;33: SYNTAX ERROR (Wildcards beim Schreiben)
ERR_WRITEFILEOPEN = 5 ;60: WRITE FILE OPEN
ERR_FILENOTOPEN = 6 ;61: FILE NOT OPEN
ERR_FILENOTFOUND = 7 ;62: FILE NOT FOUND
ERR_ILLEGALTS = 8 ;67: ILLEGAL TRACK OR SECTOR
ERR_NOCHANNEL = 9 ;70: NO CHANNEL
ERR_STARTUP = 10 ;73: Einschaltmeldung
; Datenstruktur für ein Laufwerk
STRUCTURE DRVDATA,0
BYTE drv_Type ;Laufwerks-Typ (DRTYPE_*)
BYTE drv_ReadChar ;Byte-Puffer zum Lesen aus Dateien
BYTE drv_NameLen ;Bisher empfangene Länge des Dateinamens
BYTE drv_ErrorLen ;Restliche Länge der Fehlermeldung
APTR drv_NamePtr ;Zeiger in Dateinamen, für Open/IECOut
APTR drv_ErrorPtr ;Zeiger in Fehlermeldung
APTR drv_Lock ;Lock des Verzeichnisses des Laufwerks
; bzw. Handle der .d64-Datei
;Prozeduren:
APTR drv_InitProc ; Initialisieren
APTR drv_ExitProc ; Beenden
APTR drv_OpenProc ; Kanal öffnen
APTR drv_CloseProc ; Kanal schließen
APTR drv_ReadProc ; Byte lesen
APTR drv_WriteProc ; Byte schreiben
STRUCT drv_CmdBuffer,44 ;Eingabepuffer für Kommandos
LONG drv_CmdLen ;Länge des bisher empfangenen Kommandos
STRUCT drv_Handle,16*4 ;FS: FileHandles für alle 16 Kanäle
STRUCT drv_ChanMode,16 ;D64: Kanalmodus
STRUCT drv_ChanBufNum,16 ;D64: Nummer des Puffers des Kanals
STRUCT drv_ChanBuf,16*4 ;D64: Zeiger auf Puffer des Kanals
STRUCT drv_BufPtr,16*4 ;D64: Aktuelle Position im Puffer
STRUCT drv_BufLen,16*4 ;D64: Restliche Anzahl Bytes im Puffer
APTR drv_RAM ;D64: 2KB RAM für das Laufwerk
STRUCT drv_BufFree,4 ;D64: Puffer 0..3 frei?
LONG drv_ImageHeader ;D64: Länge des Headers der Image-Datei (.d64: 0, .x64: 64)
APTR drv_BAM ;D64: Zeiger auf Puffer für BAM (Puffer 4, $700)
STRUCT drv_DIR,256 ;D64: Temporärer Puffer zum Directory-Lesen und -Scannen
LABEL drv_SIZEOF
; Drv?Type
DRTYPE_DIR = 0 ;1541-Emulation in Amiga-Verzeichnis
DRTYPE_D64 = 1 ;1541-Emulation in .d64-Datei
DRTYPE_IEC = 2 ;Echte 1541 am IEC-Kabel
; C64-Status-Codes
ST_OK = 0 ;Alles klar
ST_READ_TIMEOUT = $02 ;Timeout beim Lesen
ST_TIMEOUT = $03 ;Timeout
ST_EOF = $40 ;End of file
ST_NOTPRESENT = $80 ;Device not present
; IEC-Befehlscodes
CMD_DATA = $60
CMD_CLOSE = $e0
CMD_OPEN = $f0
; Länge des Namenspuffers, muß zwischen 32 und 256 liegen
NAMEBUF_LENGTH = 256
; Makros für IEC-Kabel
DATA_HI MACRO
bclr #5,_ciaaprb
ENDM
DATA_LO MACRO
bset #5,_ciaaprb
ENDM
CLOCK_HI MACRO
bclr #4,_ciaaprb
ENDM
CLOCK_LO MACRO
bset #4,_ciaaprb
ENDM
ATN_HI MACRO
bclr #3,_ciaaprb
ENDM
ATN_LO MACRO
bset #3,_ciaaprb
ENDM
LINE_RELEASE MACRO
ATN_HI
bsr Wait40us
CLOCK_HI
DATA_HI
ENDM
; Data -> Carry, Clock -> Negative
GET_DATA_CLOCK MACRO
\@1$ move.b _ciaaprb,d0
cmp.b _ciaaprb,d0
bne \@1$
add.b d0,d0
ENDM
WAIT_CLOCK_HI MACRO
\@1$ GET_DATA_CLOCK
bpl \@1$
ENDM
WAIT_CLOCK_LO MACRO
\@1$ GET_DATA_CLOCK
bmi \@1$
ENDM
WAIT_DATA_HI MACRO
\@1$ GET_DATA_CLOCK
bcc \@1$
ENDM
WAIT_DATA_LO MACRO
\@1$ GET_DATA_CLOCK
bcs \@1$
ENDM
**
** IEC-Emulation starten
**
** Rückgabe: d0=0 Alles OK
** d0=1 Parallelport belegt
** d0=2 Kein Timer frei
**
; Emulation aller 4 Laufwerke vorbereiten
InitIEC lea Drv8Data,a0
move.w Drv8Type,d0
bsr SetDrvProcs
pea Dir8
pea Drv8Data
move.l Drv8Data+drv_InitProc,a0
jsr (a0)
addq.l #8,sp
lea Drv9Data,a0
move.w Drv9Type,d0
bsr SetDrvProcs
pea Dir9
pea Drv9Data
move.l Drv9Data+drv_InitProc,a0
jsr (a0)
addq.l #8,sp
lea Drv10Data,a0
move.w Drv10Type,d0
bsr SetDrvProcs
pea Dir10
pea Drv10Data
move.l Drv10Data+drv_InitProc,a0
jsr (a0)
addq.l #8,sp
lea Drv11Data,a0
move.w Drv11Type,d0
bsr SetDrvProcs
pea Dir11
pea Drv11Data
move.l Drv11Data+drv_InitProc,a0
jsr (a0)
addq.l #8,sp
; Flag setzen
st.b IECInited
; Resourcen für das IEC-Kabel belegen, wenn nötig
bra CheckCablePrefs
**
** IEC-Emulation beenden
**
; Resourcen des IEC-Kabels freigeben
ExitIEC bsr CloseIECBus
; Emulation aller 4 Laufwerke beenden
tst.b IECInited
beq 1$
pea Drv8Data
move.l Drv8Data+drv_ExitProc,a0
jsr (a0)
addq.l #4,sp
pea Drv9Data
move.l Drv9Data+drv_ExitProc,a0
jsr (a0)
addq.l #4,sp
pea Drv10Data
move.l Drv10Data+drv_ExitProc,a0
jsr (a0)
addq.l #4,sp
pea Drv11Data
move.l Drv11Data+drv_ExitProc,a0
jsr (a0)
addq.l #4,sp
1$
; Temporäre Datei löschen
move.l _DOSBase,a6
move.l #TempFileName,d1
JMPLIB DeleteFile
**
** IEC-Einstellungen wurden verändert
** Rückgabe: d0=0 Alles OK
** d0=1 Parallelport belegt
** d0=2 Kein Timer frei
**
; Laufwerke schließen und neu öffnen, wenn sich die Einstellungen geändert haben
ChangedIECPrefs lea Drv8Data,a3
lea Dir8,a4
lea PDir8,a5
move.w PDrv8Type,d4
bsr DrvReInit
lea Drv9Data,a3
lea Dir9,a4
lea PDir9,a5
move.w PDrv9Type,d4
bsr DrvReInit
lea Drv10Data,a3
lea Dir10,a4
lea PDir10,a5
move.w PDrv10Type,d4
bsr DrvReInit
lea Drv11Data,a3
lea Dir11,a4
lea PDir11,a5
move.w PDrv11Type,d4
bsr DrvReInit
; Einstellungen übertragen
CheckCablePrefs moveq #0,d1
cmp.w #DRTYPE_IEC,PDrv8Type
seq d0
or.b d0,d1
cmp.w #DRTYPE_IEC,PDrv9Type
seq d0
or.b d0,d1
cmp.w #DRTYPE_IEC,PDrv10Type
seq d0
or.b d0,d1
cmp.w #DRTYPE_IEC,PDrv11Type
seq d0
or.b d0,d1
or.w OtherIEC,d1 ;d1: Flag: IEC-Kabel wird benötigt
; IEC öffnen, wenn mindestens ein Gerät mit IEC laufen soll,
; sonst schließen
tst.b d1
beq CloseIECBus
bra OpenIECBus
*
* Auf Benutzung des IEC-Kabels vorbereiten
* Rückgabe: d0=0 Alles OK
* d0=1 Parallelport belegt
* d0=2 Kein Timer frei
*
OpenIECBus move.l a6,-(sp)
tst.b IECIsOpen ;Kann auch mehrmals aufgerufen werden
bne OIECOK
; Parallelport belegen
move.l _MiscBase,d0
beq OIECNoPort
move.l d0,a6
moveq #MR_PARALLELPORT,d0
lea ParPortName,a1
JSRLIB AllocMiscResource
tst.l d0
bne OIECNoPort
move.b #$00,_ciaaprb ;Port inaktiv
move.b #$38,_ciaaddrb ;DDR konfigurieren
; Timer A belegen
move.l _CiaBase,a6
lea TimerInterrupt,a1
moveq #CIAICRB_TA,d0
JSRLIB AddICRVector
tst.l d0
bne 1$
; Gelungen
move.b #CIAICRB_TA,WhichTimer
move.b #CIAICRF_TA,WhichTimerMask
move.l #$bfe401,CiaTimerReg
move.l #$bfee01,CiaControlReg
bra 3$
; Nicht gelungen, dann Timer B probieren
1$ lea TimerInterrupt,a1
moveq #CIAICRB_TB,d0
JSRLIB AddICRVector
tst.l d0
bne 2$
; Gelungen
move.b #CIAICRB_TB,WhichTimer
move.b #CIAICRF_TB,WhichTimerMask
move.l #$bfe601,CiaTimerReg
move.l #$bfef01,CiaControlReg
bra 3$
; Nicht gelungen, dann Parallelport wieder freigeben
2$ move.b #$00,_ciaaddrb ;Port auf Eingabe
move.l _MiscBase,a6
moveq #MR_PARALLELPORT,d0
JSRLIB FreeMiscResource
moveq #2,d0 ;Kein Timer frei
move.l (sp)+,a6
rts
; Alles klar, Timer-Interrupt abschalten und Timer stoppen
3$ move.b WhichTimerMask,d0
JSRLIB AbleICR
move.l CiaControlReg,a0
and.b #$80,(a0)
st.b IECIsOpen
OIECOK moveq #0,d0 ;Alles OK
move.l (sp)+,a6
rts
OIECNoPort moveq #1,d0 ;Parallelport belegt
move.l (sp)+,a6
rts
*
* Benutzung des IEC-Kabels beenden
* Rückgabe: d0=0
*
CloseIECBus tst.b IECIsOpen
beq 1$
move.b #$00,_ciaaddrb ;Port auf Eingabe
move.l a6,-(sp) ;Parallelport freigeben
move.l _MiscBase,a6
moveq #MR_PARALLELPORT,d0
JSRLIB FreeMiscResource
move.l CiaControlReg,a0 ;Timer stoppen
and.b #$80,(a0)
move.l _CiaBase,a6
lea TimerInterrupt,a1
move.b WhichTimer,d0
JSRLIB RemICRVector
move.l (sp)+,a6
clr.b IECIsOpen
1$ moveq #0,d0
rts
*
* Laufwerk neu initialisieren, falls nötig
*
* a3.l: Zeiger auf DRVDATA
* a4.l: Alter Verzeichnis-String
* a5.l: Neuer Verzeichnis-String
* d4.w: Neues Drv?Type
*
DrvReInit move.l _UtilityBase,a6
move.l a4,a0 ;Hat sich das Verzeichnis/die Datei geändert?
move.l a5,a1
move.l #255,d0
JSRLIB Strnicmp
tst.l d0
bne 1$
cmp.b drv_Type(a3),d4 ;Nein, hat sich der Typ geändert?
beq 2$
1$ move.l a3,-(sp) ;Ja, Exit aufrufen
move.l drv_ExitProc(a3),a0
jsr (a0)
addq.l #4,sp
move.l a3,a0 ;Neue Prozeduren setzen
move.w d4,d0
bsr SetDrvProcs
move.l a5,-(sp) ;Und neues Init aufrufen
move.l a3,-(sp)
move.l drv_InitProc(a3),a0
jsr (a0)
addq.l #8,sp
2$ rts
*
* Prozeduren in DRVDATA je nach Typ setzen
*
* a0.l: Zeiger auf DRVDATA
* d0.w: Drv?Type
*
SetDrvProcs move.b d0,drv_Type(a0) ;Typ setzen
cmp.w #DRTYPE_D64,d0
beq 1$
move.l #_FS_Init,drv_InitProc(a0)
move.l #_FS_Exit,drv_ExitProc(a0)
move.l #_FS_Open,drv_OpenProc(a0)
move.l #_FS_Close,drv_CloseProc(a0)
move.l #_FS_Read,drv_ReadProc(a0)
move.l #_FS_Write,drv_WriteProc(a0)
rts
1$ move.l #_D64_Init,drv_InitProc(a0)
move.l #_D64_Exit,drv_ExitProc(a0)
move.l #_D64_Open,drv_OpenProc(a0)
move.l #_D64_Close,drv_CloseProc(a0)
move.l #_D64_Read,drv_ReadProc(a0)
move.l #_D64_Write,drv_WriteProc(a0)
rts
**
** IECOut - Ein Byte ausgeben
** d0.b: Byte
** d1.b: <0: EOI-Flag
**
IECOut tst.b ListenerIsIEC ;Listener am IEC-Kabel?
bne IECByteOut ;Ja, dann Byte über Kabel senden
tst.b ListenerActive ;Wurde ein Gerät angesprochen?
beq 1$
cmp.b #CMD_OPEN,ReceivedCmd
beq OpenOut
cmp.b #CMD_DATA,ReceivedCmd
beq DataOut
1$ move.b #ST_TIMEOUT,d0 ;Kein Listener aktiv oder kein gültiger Befehl
rts
**
** IECOutATN - Ein Byte mit ATN ausgeben (Talk/Listen/Untalk/Unlisten)
** d0.b: Byte
**
IECOutATN clr.b SecAddr ;Befehl kommt mit der Sekundäradresse danach
clr.b ReceivedCmd
move.b d0,d1
and.b #$0f,d0 ;d0.b: Gerätenummer
and.b #$f0,d1 ;d1.b: Funktion
st.b Listening ;Funktion auswerten
cmp.b #$20,d1 ;Bei Listen ist das Flag Listening gesetzt
beq Listen
clr.b Listening
cmp.b #$30,d1
beq Unlisten
cmp.b #$40,d1
beq Talk
cmp.b #$50,d1
beq Untalk
move.b #ST_TIMEOUT,d0 ;Ungültige Funktion
rts
**
** IECOutSec - Sekundäradresse ausgeben
** d0.b: Sekundäradresse
**
IECOutSec tst.b Listening ;Nach Listen oder nach Talk?
beq 1$
tst.b ListenerIsIEC ;Nach Listen: Listener am IEC-Kabel?
bne IECByteOutSec
tst.b ListenerActive ;Wurde ein Gerät angesprochen?
beq 2$
move.b d0,d1
and.b #$0f,d0 ;Sekundäradresse
move.b d0,SecAddr
and.b #$f0,d1 ;und Befehlscode speichern
move.b d1,ReceivedCmd
bra SecListen ;Sek.adr nach Listen
1$ tst.b TalkerIsIEC ;Nach Talk: Talker am IEC-Kabel?
bne IECByteOutSec
tst.b TalkerActive ;Wurde ein Gerät angesprochen?
beq 2$
move.b d0,d1
and.b #$0f,d0 ;Sekundäradresse
move.b d0,SecAddr
and.b #$f0,d1 ;und Befehlscode speichern
move.b d1,ReceivedCmd
bra SecTalk ;Sek.adr nach Talk
2$ move.b #ST_TIMEOUT,d0 ;Es erfolgte kein Listen/Talk
rts
**
** IECIn - Ein Byte einlesen
** RÜckgabe: d1.b: Byte
**
IECIn tst.b TalkerIsIEC ;Talker am IEC-Kabel?
bne IECByteIn
tst.b TalkerActive ;Wurde ein Gerät angesprochen?
beq 1$
cmp.b #CMD_DATA,ReceivedCmd
beq DataIn
1$ move.b #ST_TIMEOUT,d0 ;Kein Talk bzw. kein CMD_DATA
clr.b d1
rts
**
** IECSetATN - ATN setzen (für Untalk)
**
IECSetATN tst.b TalkerIsIEC ;Nur bei IEC-Geräten etwas machen
beq 1$
CLOCK_LO
ATN_LO
1$ rts
**
** IECRelATN - ATN wegnehmen
**
IECRelATN tst.b IECIsOpen
beq 1$
ATN_HI
bsr Wait20us
1$ rts
**
** IECTurnaround - Talk-attention turn around
**
IECTurnaround tst.b TalkerIsIEC ;Nur bei IEC-Geräten etwas machen
beq 1$
move.l a6,-(sp)
move.l _SysBase,a6
JSRLIB Disable
DATA_LO
bsr Wait20us
ATN_HI
CLOCK_HI
WAIT_CLOCK_LO
bsr Wait60us
JSRLIB Enable
move.l (sp)+,a6
1$ rts
**
** IECRelease - System line release
**
IECRelease tst.b IECIsOpen
beq 1$
LINE_RELEASE
1$ rts
*
* Listen
* d0.b: Gerätenummer
* d1.b: Funktion
*
Listen cmp.b #8,d0 ;Gerät zwischen 8 und 11?
blo ListenOther
cmp.b #11,d0
bhi ListenOther
moveq #0,d1 ;Ja, Zeiger auf Datenbereich holen
move.b d0,d1
subq.b #8,d1
move.l (DrvTab,d1.w*4),a0
cmp.b #DRTYPE_IEC,drv_Type(a0) ;IEC-Gerät?
beq ListenIEC
tst.l drv_Lock(a0) ;Nein, Verzeichnis vorhanden?
beq ListenNotPr
move.l a0,ListenerData ;Ja, Zeiger auf Datenbereich sichern
st.b ListenerActive ;Listener ist aktiv
clr.b ListenerIsIEC
moveq #ST_OK,d0
rts
; Anderes Gerät außer 8..11
ListenOther tst.w OtherIEC
beq ListenNotPr
; Listen über IEC-Kabel
ListenIEC move.l a0,ListenerData ;Zeiger auf Datenbereich sichern
moveq #$20,d1 ;Listen über Kabel senden
bsr IECByteOutATN
cmp.b #ST_NOTPRESENT,d0
beq ListenNotPr
st.b ListenerIsIEC ;Gelungen, Listener ist aktiv
rts
; Gerät nicht vorhanden
ListenNotPr move.b #ST_NOTPRESENT,d0
clr.b ListenerActive
clr.b ListenerIsIEC
rts
*
* Talk
* d0.b: Gerätenummer
* d1.b: Funktion
*
Talk cmp.b #8,d0 ;Gerät zwischen 8 und 11?
blo TalkOther
cmp.b #11,d0
bhi TalkOther
moveq #0,d1 ;Ja, Zeiger auf Datenbereich holen
move.b d0,d1
subq.b #8,d1
move.l (DrvTab,d1.w*4),a0
cmp.b #DRTYPE_IEC,drv_Type(a0) ;IEC-Gerät?
beq TalkIEC
tst.l drv_Lock(a0) ;Nein, Verzeichnis vorhanden?
beq TalkNotPr
move.l a0,TalkerData ;Ja, Zeiger auf Datenbereich sichern
st.b TalkerActive ;Talker ist aktiv
clr.b TalkerIsIEC
moveq #ST_OK,d0
rts
; Anderes Gerät außer 8..11
TalkOther tst.w OtherIEC
beq TalkNotPr
; Talk über IEC-Kabel
TalkIEC move.l a0,TalkerData ;Zeiger auf Datenbereich sichern
moveq #$40,d1 ;Talk über Kabel senden
bsr IECByteOutATN
cmp.b #ST_NOTPRESENT,d0
beq TalkNotPr
st.b TalkerIsIEC ;Gelungen, Talker ist aktiv
rts
; Gerät nicht vorhanden
TalkNotPr move.b #ST_NOTPRESENT,d0
clr.b TalkerActive
clr.b TalkerIsIEC
rts
*
* Unlisten
* d0.b: Gerätenummer
* d1.b: Funktion
*
Unlisten tst.b ListenerIsIEC ;Listener am IEC-Kabel?
beq 1$
bsr IECByteOutATN ;Ja, Unlisten über Kabel senden
bra 2$
1$ moveq #ST_OK,d0
2$ clr.b ListenerActive ;Gerät nicht mehr aktiv
clr.b ListenerIsIEC
rts
*
* Untalk
* d0.b: Gerätenummer
* d1.b: Funktion
*
Untalk tst.b TalkerIsIEC ;Talker am IEC-Kabel?
beq 1$
bsr IECByteOutATN ;Ja, Untalk über Kabel senden
bra 2$
1$ moveq #ST_OK,d0
2$ clr.b TalkerActive ;Gerät nicht mehr aktiv
clr.b TalkerIsIEC
rts
*
* SecListen - Sekundäradresse nach Listen
* d0.b: Sekundäradresse
* d1.b: Befehlscode
*
SecListen cmp.b #CMD_OPEN,d1
beq SecListenOpen
cmp.b #CMD_CLOSE,d1
beq SecListenClose
moveq #ST_OK,d0
rts
*
* Open-Befehl: Auf Empfang des Dateinamens vorbereiten
*
SecListenOpen move.l ListenerData,a0
move.l #NameBuf,drv_NamePtr(a0)
clr.b drv_NameLen(a0)
moveq #ST_OK,d0
rts
*
* Close-Befehl: Kanal schließen
*
SecListenClose move.l ListenerData,a0
moveq #0,d0
move.b SecAddr,d0
move.l d0,-(sp)
move.l a0,-(sp)
move.l drv_CloseProc(a0),a0
jsr (a0)
addq.l #8,sp
rts
*
* SecTalk - Sekundäradresse nach Talk
* d0.b: Sekundäradresse
* d1.b: Befehlscode
*
SecTalk moveq #ST_OK,d0
rts
*
* Byte nach Open-Befehl: Zeichen im Dateinamen speichern, bei EOI Datei öffnen
* d0.b: Byte
* d1.b: EOI-Flag
*
OpenOut move.l ListenerData,a0
cmp.b #NAMEBUF_LENGTH-1,drv_NameLen(a0) ;Noch genügend Platz im NameBuffer?
bhs 2$
move.l drv_NamePtr(a0),a1 ;Ja, Zeichen speichern
move.b d0,(a1)+
move.l a1,drv_NamePtr(a0)
addq.b #1,drv_NameLen(a0)
2$ tst.b d1 ;EOI?
bpl 1$
move.l ListenerData,a0 ;Ja, Datei öffnen
move.l drv_NamePtr(a0),a1 ;Dateiname mit Nullbyte abschließen
clr.b (a1)
pea NameBuf
moveq #0,d0
move.b SecAddr,d0
move.l d0,-(sp)
move.l a0,-(sp)
move.l drv_OpenProc(a0),a0
jsr (a0)
lea 12(sp),sp
1$ moveq #ST_OK,d0
rts
*
* Zeichen aus Datenkanal lesen
* RÜckgabe: d1.b: Byte
*
DataIn move.l TalkerData,a0
moveq #0,d0
move.b SecAddr,d0
pea DataInBuf
move.l d0,-(sp)
move.l a0,-(sp)
move.l drv_ReadProc(a0),a0
jsr (a0)
lea 12(sp),sp
move.b DataInBuf,d1
rts
*
* Byte nach Data-Befehl: Zeichen in Datei schreiben
* d0.b: Byte
* d1.b: EOI-Flag
*
DataOut move.l ListenerData,a0
move.l d1,-(sp)
move.l d0,-(sp)
moveq #0,d0
move.b SecAddr,d0
move.l d0,-(sp)
move.l a0,-(sp)
move.l drv_WriteProc(a0),a0
jsr (a0)
lea 16(sp),sp
rts
*
* Byte mit ATN über IEC-Kabel senden
* d0.b: Gerätenummer
* d1.b: Funktion
*
IECByteOutATN tst.b IECIsOpen
bne 1$
move.b #ST_NOTPRESENT,d0
rts
1$ movem.l d2/d7/a6,-(sp)
move.b d0,d2
or.b d1,d2 ;d2: Auszugebendes Byte
move.l _SysBase,a6
JSRLIB Disable
DATA_HI
ATN_LO
bra IECByteOutSec2 ;ATN response abwarten und Byte ausgeben
*
* Sekundäradresse über IEC-Kabel senden
* d0.b: Sekundäradresse
*
IECByteOutSec tst.b IECIsOpen
bne 1$
move.b #ST_NOTPRESENT,d0
rts
1$ movem.l d2/d7/a6,-(sp)
move.b d0,d2 ;d2: Auszugebendes Byte
move.l _SysBase,a6
JSRLIB Disable
IECByteOutSec2 CLOCK_LO
DATA_HI
bsr Wait1ms ;Max ATN response abwarten
moveq #0,d1 ;Kein EOI
bra IECByteOut2 ;Byte ausgeben
*
* Byte über IEC-Kabel senden
* d0.b: Byte
* d1.b: EOI-Flag
*
IECByteOut tst.b IECIsOpen
bne 1$
move.b #ST_NOTPRESENT,d0
rts
1$ movem.l d2/d7/a6,-(sp)
move.b d0,d2 ;d2: Auszugebendes Byte
move.l _SysBase,a6
JSRLIB Disable
; Übertragung einleiten
IECByteOut2 move.b d1,d7 ;d7: EOI-Flag
DATA_HI
GET_DATA_CLOCK ;Data bleibt high -> Device not present
bcs IBODevNotPr
CLOCK_HI ;Talker ready
WAIT_DATA_HI ;Warten auf Listener ready for data
tst.b d7 ;EOI?
bpl 1$
WAIT_DATA_LO ;Ja, EOI-Handshake abwarten
WAIT_DATA_HI
1$ CLOCK_LO ;Talker sending
bsr Wait40us ;Wichtige Verzögerung, aber wozu?
; 8 Bits übertragen
moveq #7,d7 ;d7: Bitzähler
IBOBitLoop GET_DATA_CLOCK ;Data low -> Time out
bcc IBOTimeOut
lsr.b #1,d2 ;Nächstes Bit holen
bcc 1$ ;und ausgeben
DATA_HI
bra 2$
1$ DATA_LO
2$
bsr Wait40us ;Bit set-up talker delay
CLOCK_HI ;Data valid
bsr Wait20us ;Data valid delay
DATA_HI
CLOCK_LO
dbra d7,IBOBitLoop ;Nächstes Bit
; 1ms auf Bestätigung warten
bsr Start1ms ;Timer starten
IBODACLoop bsr TimerDoneQ ;Timer abgelaufen?
bne IBOTimeOut ;Ja, dann Time out
GET_DATA_CLOCK ;Auf Listener data accepted warten
bcs IBODACLoop
; Alles OK
moveq #ST_OK,d0
bra IBODone
; Fehlerbehandlung
IBODevNotPr LINE_RELEASE
move.b #ST_NOTPRESENT,d0
bra IBODone
IBOTimeOut LINE_RELEASE
move.b #ST_TIMEOUT,d0
IBODone JSRLIB Enable
movem.l (sp)+,a6/d2/d7
rts
*
* Byte über IEC-Kabel empfangen
* Rückgabe: d1.b: Empfangenes Byte
*
IECByteIn tst.b IECIsOpen
bne 1$
move.b #ST_NOTPRESENT,d0
rts
1$ movem.l d2/d3/d7/a6,-(sp)
moveq #ST_OK,d3 ;d3: Status
move.l _SysBase,a6
JSRLIB Disable
CLOCK_HI
WAIT_CLOCK_HI
; Auf Beginn der Übertragung warten
bsr Start250us ;Timer starten
DATA_HI ;Listener ready for data
IBIWaitTalker bsr TimerDoneQ ;Timer abgelaufen?
bne IBIEOI ;Ja, EOI empfangen
GET_DATA_CLOCK ;Warten auf talker sending
bmi IBIWaitTalker
bra IBIReceive
; EOI empfangen, Handshake senden und erneut auf Beginn warten
IBIEOI move.b #ST_EOF,d3
DATA_LO ;EOI handshake
CLOCK_HI
bsr Wait60us ;EOI response hold time
bsr Start250us ;Timer starten
DATA_HI ;Listener ready for data
IBIWaitTalker2 bsr TimerDoneQ ;Timer abgelaufen?
bne IBITimeOut ;Ja, dann Time out
GET_DATA_CLOCK ;Warten auf talker sending
bmi IBIWaitTalker2
; 8 Bits empfangen
IBIReceive moveq #7,d7 ;d7: Bitzähler
moveq #0,d2 ;d2: Empfangenes Byte
IBIBitLoop WAIT_CLOCK_HI ;Auf Data valid warten
roxr.b #1,d2 ;Datenbit holen
WAIT_CLOCK_LO
dbra d7,IBIBitLoop
; Empfang bestätigen
DATA_LO ;Listener data accepted
btst #6,d3 ;EOI empfangen?
beq IBIDone
LINE_RELEASE ;Ja, Leitung freigeben
bra IBIDone
; Fehlerbehandlung
IBITimeOut or.b #ST_READ_TIMEOUT,d3
LINE_RELEASE
IBIDone move.b d3,d0 ;Status holen
move.b d2,d1 ;Byte holen
JSRLIB Enable
movem.l (sp)+,d2/d3/d7/a6
rts
*
* 20µs/40µs/60µs/1ms warten
*
; Timer auf 20µs setzen
Wait20us move.l CiaControlReg,a0
and.b #$80,(a0)
move.l CiaTimerReg,a0
move.b #14,(a0)
move.b #0,$100(a0)
bra StartTimer
; Timer auf 40µs setzen
Wait40us move.l CiaControlReg,a0
and.b #$80,(a0)
move.l CiaTimerReg,a0
move.b #29,(a0)
move.b #0,$100(a0)
bra StartTimer
; Timer auf 60µs setzen
Wait60us move.l CiaControlReg,a0
and.b #$80,(a0)
move.l CiaTimerReg,a0
move.b #43,(a0)
move.b #0,$100(a0)
bra StartTimer
; Timer auf 1ms setzen
Wait1ms move.l CiaControlReg,a0
and.b #$80,(a0)
move.l CiaTimerReg,a0
move.b #$cc,(a0)
move.b #2,$100(a0)
move.l CiaControlReg,a0
; Timer anwerfen (One-Shot) und auf Ablauf warten
StartTimer move.l a6,-(sp)
move.l _CiaBase,a6
move.b WhichTimerMask,d0
JSRLIB SetICR
move.l CiaControlReg,a0
move.b #CIACRAF_LOAD|CIACRAF_RUNMODE|CIACRAF_START,(a0)
1$ moveq #0,d0
JSRLIB SetICR
and.b WhichTimerMask,d0
beq 1$
move.l (sp)+,a6
rts
*
* Timer starten, 1ms/250µs
*
Start1ms move.l a6,-(sp)
move.l _CiaBase,a6
move.l CiaControlReg,a0
and.b #$80,(a0)
move.l CiaTimerReg,a0
move.b #$cc,(a0)
move.b #2,$100(a0)
move.b WhichTimerMask,d0
JSRLIB SetICR
move.l CiaControlReg,a0
or.b #CIACRAF_LOAD|CIACRAF_RUNMODE|CIACRAF_START,(a0)
move.l (sp)+,a6
rts
Start250us move.l a6,-(sp)
move.l _CiaBase,a6
move.l CiaControlReg,a0
and.b #$80,(a0)
move.l CiaTimerReg,a0
move.b #$b3,(a0)
move.b #0,$100(a0)
move.b WhichTimerMask,d0
JSRLIB SetICR
move.l CiaControlReg,a0
or.b #CIACRAF_LOAD|CIACRAF_RUNMODE|CIACRAF_START,(a0)
move.l (sp)+,a6
rts
*
* Timer abgelaufen?
* bne: Ja
*
TimerDoneQ move.l a6,-(sp)
move.l _CiaBase,a6
moveq #0,d0
JSRLIB SetICR
and.b WhichTimerMask,d0
move.l (sp)+,a6
tst.b d0
rts
*
* Dummy-Timer-Interrupt-Routine
*
IntTimer moveq #0,d0
rts
*
* SetError: 1541-Fehlermeldung bereitstellen
*
* a0.l: Zeiger auf DRVDATA
* d0.l: Fehlercode
*
XDEF _SetError
_SetError move.l 4(sp),a0
move.l 8(sp),d0
lea ErrorTab,a1
move.l (a1,d0.l*4),a1
move.l a1,drv_ErrorPtr(a0)
move.l a2,-(sp)
move.l a1,a2
1$ tst.b (a2)+
bne 1$
sub.l a1,a2
move.l a2,d0
subq.l #1,d0
move.b d0,drv_ErrorLen(a0)
move.l (sp)+,a2
rts
**
** Konstanten
**
; Tabelle für Fehlermeldungen (Index: ErrorCode)
CNOP 0,4
ErrorTab dc.l OKText
dc.l WriteErrText
dc.l WriteProtText
dc.l Syntax30Text
dc.l Syntax33Text
dc.l WrFileOpenText
dc.l FileNotOpenText
dc.l FileNotFText
dc.l IllegalTSText
dc.l NoChannelText
dc.l StartupText
; Fehlertexte
OKText dc.b "00, OK,00,00",13,0
WriteErrText dc.b "25,WRITE ERROR,00,00",13,0
WriteProtText dc.b "26,WRITE PROTECT ON,00,00",13,0
Syntax30Text dc.b "30,SYNTAX ERROR,00,00",13,0
Syntax33Text dc.b "33,SYNTAX ERROR,00,00",13,0
WrFileOpenText dc.b "60,WRITE FILE OPEN,00,00",13,0
FileNotOpenText dc.b "61,FILE NOT OPEN,00,00",13,0
FileNotFText dc.b "62,FILE NOT FOUND,00,00",13,0
IllegalTSText dc.b "67,ILLEGAL TRACK OR SECTOR,00,00",13,0
NoChannelText dc.b "70,NO CHANNEL,00,00",13,0
StartupText dc.b "73,CBM DOS V2.6 1541,00,00",13,0
CNOP 0,4
; Tabelle mit Zeigern auf die Laufwerks-Datenstrukturen
CNOP 0,4
DrvTab dc.l Drv8Data
dc.l Drv9Data
dc.l Drv10Data
dc.l Drv11Data
; Name der temporären Datei für das Directory
TempFileName dc.b "T:Frodo$File",0
; Name für AllocMiscResource
ParPortName dc.b "Frodo Parallel Port",0
; Name des Timer-Interrupts
TimerIntName dc.b "Frodo Timer Int",0
CNOP 0,4
**
** Initialisierte Daten
**
SECTION "DATA",DATA
TimerInterrupt dc.l 0,0
dc.b NT_INTERRUPT,0
dc.l TimerIntName
dc.l 0
dc.l IntTimer
**
** Nicht initialisierte Daten
**
SECTION "BSS",BSS
; Global
ListenerActive ds.b 1 ;Flag: Listener angewählt, ListenerData ist gültig
ListenerIsIEC ds.b 1 ;Flag: Aktiver Listener hängt am IEC-Kabel (impliziert ListenerActive)
TalkerActive ds.b 1 ;Flag: Talker angewählt, TalkerData ist gültig
TalkerIsIEC ds.b 1 ;Flag: Aktiver Talker hängt am IEC-Kabel (impliziert TalkerActive)
Listening ds.b 1 ;Flag: Letzter ATN-Befehl war Listen (für SecListen/SecTalk-Unterscheidung)
SecAddr ds.b 1 ;Aktuelle Sekundäradresse ($0x)
ReceivedCmd ds.b 1 ;Empfangener Befehlscode ($x0)
IECIsOpen ds.b 1 ;Flag: Timer und Port belegt, IEC-Kabel kann benutzt werden
IECInited ds.b 1 ;Flag: InitIEC wurde aufgerufen
WhichTimer ds.b 1 ;Welcher Timer wurde belegt? (0: Timer A, 1: Timer B)
WhichTimerMask ds.b 1 ;Dto. als Bit-Maske für ICR
CNOP 0,4
CiaTimerReg ds.l 1 ;Zeiger auf Cia-Timer-Register
CiaControlReg ds.l 1
ListenerData ds.l 1 ;Zeiger auf Struktur des aktiven Listeners
TalkerData ds.l 1 ;Zeiger auf Struktur des aktiven Talkers
DataInBuf ds.b 1 ;Puffer für DataIn
; Datenstruktur pro Laufwerk
CNOP 0,4
Drv8Data ds.b drv_SIZEOF
Drv9Data ds.b drv_SIZEOF
Drv10Data ds.b drv_SIZEOF
Drv11Data ds.b drv_SIZEOF
; Puffer für Dateinamen/Befehlsstrings
NameBuf ds.b NAMEBUF_LENGTH
; Puffer für die Verzeichnisse der 4 Laufwerke
Dir8 ds.b 256
Dir9 ds.b 256
Dir10 ds.b 256
Dir11 ds.b 256
; Einstellungen
Drv8Type ds.w 1
Drv9Type ds.w 1
Drv10Type ds.w 1
Drv11Type ds.w 1
OtherIEC ds.w 1
XDEF _MapSlash
_MapSlash
MapSlash ds.w 1
END